#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "thread_list.h"
#include "common.h"


thread_list* thread_list_create() {
	thread_list* list;
	if (!(list = (thread_list*) malloc (sizeof (thread_list)))) {
		return NULL;
	}
	list->dummy.entry = NULL;
	list->dummy.next = NULL;
	list->dummy.prev = NULL;
	list->size = 0;
	return list;
}
int thread_list_destroy(thread_list* list) {
	if (!list) {
		return HW3_INVALID_PARAM;
	}
	while (list->size) {
		thread_list_remove_head (list);
	}
	free(list);
	return HW3_SUCCESS;
}
node* thread_list_insert_head(thread_list* list, void* entry) {
	if (!list) {
		return (node*)HW3_INVALID_PARAM;
	}
	node *n;
	if (!(n = (node*) malloc (sizeof(node)))) {
		return (node*)HW3_NOMEM;
	}
	n->entry = entry;
	node* head = thread_list_head(list);
	node* tail = thread_list_tail(list);
	if (!head) { //empty list
		assert(!tail);
		list->dummy.next = n;
		list->dummy.prev = n;
		n->next = NULL;
		n->prev = NULL;
	} else {
		assert(tail);
		n->next = head;
		head->prev = n;
		n->prev = NULL;
		list->dummy.next = n;
	}
	list->size++;
	return n;
}
node* thread_list_insert_tail(thread_list* list, void* entry) {
	if (!list) {
		return (node*)HW3_INVALID_PARAM;
	}
	node *n;
	if (!(n = (node*) malloc (sizeof(node)))) {
		return (node*)HW3_NOMEM;
	}
	n->entry = entry;
	node* head = thread_list_head(list);
	node* tail = thread_list_tail(list);
	if (!head) { //empty list
		assert(!tail);
		list->dummy.next = n;
		list->dummy.prev = n;
		n->next = NULL;
		n->prev = NULL;
	} else {
		assert(tail);
		n->next = NULL;
		n->prev = tail;
		tail->next = n;
		list->dummy.prev = n;
	}
	list->size++;
	return n;
}
node* thread_list_insert_after(thread_list* list, node* n, void* entry) {
	if (!n || !list) {
		return (node*)HW3_INVALID_PARAM;
	}
	node *newNode;
	if (!(newNode = (node*) malloc (sizeof(node)))) {
		return (node*)HW3_NOMEM;
	}
	newNode->entry = entry;
	newNode->next = n->next;
	node* tail = thread_list_tail(list);
	if (n != tail) {
		assert(n->next);
		n->next->prev = newNode;
	} else {
		list->dummy.prev = newNode;
	}
	n->next = newNode;
	newNode->prev = n;

	list->size++;
	return newNode;
}
void* thread_list_remove_tail(thread_list* list) {
	void* entry;
	if (!list) {
		return NULL;
	}
	node* head = thread_list_head(list);
	node* tail = thread_list_tail(list);
	if (!head) { //empty list
		return NULL;
	}
	if (list->size == 1) {
		list->dummy.prev = NULL;
		list->dummy.next = NULL;
	} else {
		assert(tail);
		assert(tail->prev);
		tail->prev->next = NULL;
		list->dummy.prev = tail->prev;
	}
	entry = tail->entry;
	free (tail);
	list->size--;
	
	return entry;
}
void* thread_list_remove_head(thread_list* list) {
	void* entry;
	if (!list) {
		return (void*)HW3_INVALID_PARAM;
	}
	node* head = thread_list_head(list);
	node* tail = thread_list_tail(list);
	if (!head) { //empty list
		return (void*)HW3_FAILURE;
	}
	if (list->size == 1) {
		list->dummy.prev = NULL;
		list->dummy.next = NULL;
	} else {
		assert(head);
		assert(head->next);
		head->next->prev = NULL;
		list->dummy.next = head->next;
	}
	entry = head->entry;
	free (head);
	list->size--;
	return entry;
}

//void* thread_list_remove_after(thread_list *list, node* n) {
//	void* entry;
//	if (!list || !n || !n->next) {
//		return (void*)HW3_INVALID_PARAM;
//	}
//	assert(list->size);
//	node* toRemove = n->next;
//	assert (list->size > 1);
//	n->next = toRemove->next;
//	toRemove->next->prev = n;
//	if (toRemove == thread_list_head(list)) {
//		list->dummy.next = toRemove->next;
//	}
//	if (toRemove == thread_list_tail(list)) {
//		list->dummy.prev = toRemove->prev/*==n*/;
//	}
//	entry = toRemove->entry;
//	free (toRemove);
//	list->size--;
//	return entry;
//}

node* thread_list_head(thread_list* list) {
	if (!list) {
		return (node*)HW3_INVALID_PARAM;
	}
	return list->dummy.next;
}

node* thread_list_tail(thread_list* list) {
	if (!list) {
		return (node*)HW3_INVALID_PARAM;
	}
	return list->dummy.prev;
}

int thread_list_size(thread_list* list) {
	if (!list) {
		return HW3_INVALID_PARAM;
	}
	return list->size;
}

int thread_list_is_in(thread_list* list, void* entry) {
	if (!list) {
		return HW3_INVALID_PARAM;
	}
	return (thread_list_find(list, entry) ? 1 : 0);
}

node* thread_list_find(thread_list* list, void* entry) {
	node *n;
	if (!list) {
		return (node*)HW3_INVALID_PARAM;
	}
	thread_list_for_each (list, n) {
		if (n->entry == entry) {
			return n;
		}
	}
	return NULL;
}

int thread_list_is_empty(thread_list* list) {
	if (!list) {
		return HW3_INVALID_PARAM;
	}
	return ((list->size == 0) ? 1 : 0);
}

void* thread_list_remove(thread_list* list, void* entry) {
	node* n;
	if (!list) {
		return NULL;
	}
	n = thread_list_find(list, entry);
	if (!n) {
		return NULL;
	}
	return thread_list_remove_node(list, n);
}

void* thread_list_remove_node(thread_list *list, node* n) {
	void* entry;
	if (!list || !n) {
		return NULL;
	}
	assert(list->size > 0);
	node *before_n =  n->prev;
	node *after_n =  n->next;
	if (before_n != NULL) {
		before_n->next = after_n;
	} else {
		assert (n == thread_list_head(list));
		list->dummy.next = after_n;
	}
	if (after_n != NULL) {
		after_n->prev = before_n;
	} else {
		assert (n == thread_list_tail(list));
		list->dummy.prev = before_n;
	}
	entry = n->entry;
	free (n);
	list->size--;
	return entry;
}

